package com.boot2.core.service;

import com.boot2.core.constant.CoreConstant;
import com.boot2.core.model.AbstractLog;
import com.boot2.core.model.dto.ClientDeviceInfo;
import com.boot2.core.utils.BeanUtils;
import com.boot2.core.utils.CheckUtil;
import com.boot2.core.utils.JwtUtil;
import com.boot2.core.utils.ServletUtil;
import com.boot2.core.utils.SpringUtil;
import com.boot2.core.utils.StringUtils;
import com.boot2.core.utils.ip.IPAddressUtils;
import com.boot2.core.utils.ip.IPLocation;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author zhangweilin
 * @description: aop service
 * @date 2019/3/18
 */
@Component
@EnableAsync
public class AopService {

    /**
     * 将request放入线程变量中
     */
    final static ThreadLocal<HttpServletRequest> httpRequestThreadLocal = new ThreadLocal<>();

    @Autowired
    IPAddressUtils ipAddressUtils;

    @Autowired
    Environment environment;

    /**
     * 处理用户操作的全部日志并入库
     *
     * @param pjp
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    @Async
    public void handLog(Object handler, HttpServletRequest request, ProceedingJoinPoint pjp) {
        httpRequestThreadLocal.set(request);
        String userIdStr = request.getHeader(JwtUtil.USER_ID);
        ClientDeviceInfo clientDeviceInfo = ServletUtil.getClientDeviceInfo(request);
        AbstractLog abstractLog = (AbstractLog) request.getAttribute(CoreConstant.userLog);
        if (null == abstractLog) {
            httpRequestThreadLocal.remove();
            return;
        }
        BeanUtils.copyProperties(clientDeviceInfo, abstractLog);

        if (handler != null && handler instanceof HandlerMethod) {
            Object bean = SpringUtil.getBean(AbstractLogService.class);
//            Class aClass = (Class) ((ParameterizedType) bean.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
            //由于此时的Bean为spring的代理对象，所以无法直接获取到泛型类型，此获取到的为代理目标的真实class,(Class为Type的子类)，要想获得泛型类型，需要在此基础上再次去获取
//            Type genericSuperclass = bean.getClass().getGenericSuperclass();
//            Class<? extends AbstractLog> logModelClazz = (Class<? extends AbstractLog>) ((ParameterizedType) ((Class<?>) genericSuperclass).getGenericSuperclass()).getActualTypeArguments()[0];
            if (null == abstractLog) {
                return;
            }
            if (bean instanceof AbstractLogService) {
                AbstractLogService<AbstractLog> abstractLogService = (AbstractLogService<AbstractLog>) bean;
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                Class<?> beanType = handlerMethod.getBeanType();
                String className = beanType.getName();
                abstractLog.setClassName(className);
                Method method = handlerMethod.getMethod();
                abstractLog.setMethodName(method.toString());
                MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
                Object[] args = pjp.getArgs();
                Map<String, String> paramMap = new HashMap<>();
                if (null != args && null != methodParameters) {
                    for (int i = 0; i < methodParameters.length; i++) {
                        String parameterName = methodParameters[i].getParameterName();
                        paramMap.put(parameterName, ArrayUtils.toString(args[i]));
                    }
                }
                abstractLog.setParam(paramMap.toString());
                Api api = beanType.getAnnotation(Api.class);

                if (null != api) {
                    String[] tags = api.tags();
                    abstractLog.setModuleName(tags[0]);
                }
                ApiOperation[] apiOperations = method.getDeclaredAnnotationsByType(ApiOperation.class);
                if (null != apiOperations && apiOperations.length > 0) {
                    String funcName = apiOperations[0].value();
                    String funcDesc = apiOperations[0].notes();
                    abstractLog.setFuncName(funcName);
                    abstractLog.setFuncDesc(funcDesc);
                }
//                abstractLog.setCreateTime();
                if (StringUtils.isNotEmpty(userIdStr)) {
                    abstractLog.setObjectId(userIdStr);
                }

                String ip = ServletUtil.getIp(request);
                if (CheckUtil.isNotEmpty(ip)) {
                    abstractLog.setIp(ip);
                    IPLocation ipLocation = ipAddressUtils.getIPLocation(ip);
                    String country = ipLocation.getCountry();
                    String province = ipLocation.getProvince();
                    String city = ipLocation.getCity();
                    String area = ipLocation.getArea();
                    abstractLog.setCountry(country);
                    abstractLog.setProvinceName(province);
                    abstractLog.setCityName(city);
                    abstractLog.setCarrier(area);
                }
                abstractLog.setServerAppName(environment.getProperty("spring.application.name"));
                abstractLogService.saveLog(abstractLog);
            }
        }
        httpRequestThreadLocal.remove();
    }
}
